#include <cstdlib>
#include <GL/glut.h>

#include "PfMap.h"

PfMap map;
int g_windowWidth(576);
int g_windowHeight(576);
std::vector<PfSegment> g_segments;
bool g_createSegment(false);
bool g_showSegments(true);
char g_saveFileNumber('1');

static PfVec2 WindowToMap(const PfVec2 & point)
{
	return PfVec2(point.x-(g_windowWidth-map.width)/2, point.y-(g_windowHeight-map.height)/2);
}

static PfVec2 MapToWindow(const PfVec2 & point)
{
	return PfVec2(point.x+(g_windowWidth-map.width)/2, point.y+(g_windowHeight-map.height)/2);
}

void OnKeyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
	case 's':
		g_showSegments = !g_showSegments;
		break;
	case 'c':
		g_segments.clear();
		map.MakeRegions(g_segments);
		break;
	case 'w':
		{
			std::string filename("graph"); filename+=g_saveFileNumber; filename+=".dat";
			g_saveFileNumber++;
			map.SaveGraph(filename.c_str());
		}
		break;
	}
}

void OnMouse(int button, int state, int x, int y)
{
	const PfVec2 point(WindowToMap(PfVec2(x,y)));
	if(button == GLUT_LEFT_BUTTON)
	{
		if(state == GLUT_DOWN)
		{
			g_segments.push_back(PfSegment(point,point));
			g_createSegment = true;
		}
		else
		{
			map.MakeRegions(g_segments);
			g_createSegment = false;
		}
	}
	else if(button == GLUT_RIGHT_BUTTON)
	{
		if(state == GLUT_DOWN)
		{
			if(!g_segments.empty())
			{
				g_segments.pop_back();
				map.MakeRegions(g_segments);
			}
		}
	}
}

void OnMotion(int x, int y)
{
	if(g_createSegment)
	{
		g_segments.back().v1 = WindowToMap(PfVec2(x,y));
	}
}

void OnReshape(int w, int h)
{
	g_windowWidth = w;
	g_windowHeight = h;
	glViewport(0,0,w,h);
}

#include <string>
#include <sstream>

void DrawString(const PfVec2 & coords, const std::string & text)
{
	glRasterPos2iv(coords);
	for(size_t i(0); i<text.length(); ++i)
	{
		glutBitmapCharacter(GLUT_BITMAP_8_BY_13, text[i]);
	}
}

void OnDraw() 
{
	static const float colors[][3] = {{0,0,0},{1,0,0},{0,1,0},{0,0,1},{1,1,0},{1,1,1}};
	static const float * regionColors[256];
	for(int i(0); i<251; i+=3)
	{
		regionColors[i] = colors[1];
		regionColors[i+1] = colors[2];
		regionColors[i+2] = colors[3];
	}
	regionColors[253] = colors[0];
	regionColors[254] = colors[4];
	regionColors[255] = colors[5];

	glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glOrtho(0,g_windowWidth,g_windowHeight,0,-1,1);
	glMatrixMode(GL_MODELVIEW);

	glPushMatrix();
	glTranslatef((g_windowWidth-map.width)/2,(g_windowHeight-map.height)/2,0);
	glBegin(GL_POINTS);
	for(int y(0); y<map.height; ++y)
	{
		for(int x(0); x<map.width; ++x)
		{
			glColor3fv(regionColors[map(x,y)]);
			glVertex2i(x,y);
		}
	}
	glEnd();

	if(g_showSegments)
	{
		glEnable(GL_LINE_SMOOTH);
		glLineWidth(2.0f);
		glBegin(GL_LINES);
		for(size_t i(0); i<g_segments.size(); ++i)
		{
			const PfSegment & seg(g_segments[i]);
			glColor3fv(regionColors[TILE_BORDER]);
			glVertex2iv(seg.v0);
			glVertex2iv(seg.v1);
		}
		glEnd();
	}

	glColor3f(0.8f,0.0f,1.0f);
	glBegin(GL_LINES);
	for(size_t i(0); i<map.borders.size(); ++i)
	{
		for(size_t j(0); j<map.borders.size(); ++j)
		{
			const PfMap::Edge & edge(map.GetEdge(i,j));
			if(edge.connected)
			{
				glVertex2iv(map.GetBorder(i).center);
				glVertex2iv(map.GetBorder(j).center);
			}
		}
	}
	glEnd();

	glPopMatrix();

	glColor3f(1,1,1);
	std::ostringstream out;
	out << map.numRegions << " regions, " << map.borders.size() << " borders";
	DrawString(PfVec2(20,20), out.str());

	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glPopAttrib();
	glutSwapBuffers();

	// glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, '@');
}

void OnIdle()
{
	/*static int lastStep(0);
	int time = glutGet(GLUT_ELAPSED_TIME);
	float timestep = (float)(time - lastStep) / 1000.0f;
	lastStep = time;*/

	glutPostRedisplay();
}

int main(int argc, char * argv[]) 
{
	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_DEPTH|GLUT_RGBA|GLUT_DOUBLE);
	glutInitWindowSize(g_windowWidth,g_windowHeight);
	glutCreateWindow("A basic primitive example");

	glutDisplayFunc(OnDraw);
	glutReshapeFunc(OnReshape);
	glutIdleFunc(OnIdle);
	glutKeyboardFunc(OnKeyboard);
	glutMouseFunc(OnMouse);
	glutMotionFunc(OnMotion);

	if(!map.Load("map2_big.map")) return -1;
	map.MakeRegions(g_segments);

	glutMainLoop();

	return 0;
}